// Implementation of an icon for Gtk+
// Copyright 2006 (C) Ralph Thomas

#include <toolkit/gtk/icon-gtk.h>
#include <map>
#include <iostream>
#include <boost/filesystem/operations.hpp>
#include <egg-pixbuf-thumbnail.h>

namespace bfs = boost::filesystem;

namespace toolkit {
	icon_gtk::icon_gtk( const std::string& icon_name, GdkPixbuf* preview ) : icon_name_m( icon_name ), preview_m( preview ) {}
	icon_gtk::~icon_gtk() {
		if( preview_m ) g_object_unref( preview_m );
	}
	void icon_gtk::apply( GtkImage* image, GtkIconSize size ) const {
		if( !preview_m )
			gtk_image_set_from_icon_name( image, icon_name_m.c_str(), size );
		else // XXX: !!!
			gtk_image_set_from_file( image, icon_name_m.c_str() );
	}
	GdkPixbuf* icon_gtk::get_pixbuf( long pixel_size ) const {
		if( preview_m ) {
			g_object_ref( preview_m );
			return preview_m;
		}
		GtkIconTheme* theme( gtk_icon_theme_get_default() );
		return gtk_icon_theme_load_icon( theme, icon_name_m.c_str(), pixel_size, (GtkIconLookupFlags)0, 0 );
	}
	
	adobe::value_t getIcon( const std::string& type, const bfs::path& file ) {
		typedef std::map<std::string, std::string>	mime_to_gnome_t;
		static mime_to_gnome_t	gnome_icon_db_s;
		static bool		initialized_s( false );
		static const char*	fallback_s( "gnome-mime-application-octet-stream" );
		GtkIconTheme*		theme( gtk_icon_theme_get_default() );
		//
		// If a preview exists and is valid then use that.
		//
//		std::string uri( "file://" ); uri += file.native_file_string();
//		GdkPixbuf* preview = egg_pixbuf_load_thumbnail( uri.c_str(), bfs::last_write_time( file ), EGG_PIXBUF_THUMBNAIL_NORMAL );
//		if( preview ) return adobe::value_t( icon_gtk_ptr_t( new icon_gtk( "", preview ) ) );
		//
		// We only pay attention to the MIME type here. We use some
		// rules that I stole from thunar-vfs to look up the icon
		// name in a GNOME icon theme. This seems like the best thing
		// to do to fit into a regular GTK+ desktop.
		//
		// First, initialize our database of GNOME icon names.
		//
		if( !initialized_s ) {
			gnome_icon_db_s["inode/blockdevice"] = "gnome-fs-blockdev";
			gnome_icon_db_s["inode/chardevice"] = "gnome-fs-chardev";
			gnome_icon_db_s["inode/directory"] = "gnome-fs-directory";
			gnome_icon_db_s["inode/fifo"] = "gnome-fs-fifo";
			gnome_icon_db_s["inode/socket"] = "gnome-fs-socket";
			initialized_s = true;
		}
		if( type.find( '/' ) == std::string::npos ) {
			//
			// We need a real MIME type to locate an icon.
			//
			return adobe::value_t( icon_gtk_ptr_t( new icon_gtk( fallback_s ) ) );
		}
		//
		// First we try gnome-mime-<media>-<subtype>, then just
		// gnome-mime-<media>, then we give up and use the
		// fallback.
		//
		std::string media( type.substr( 0, type.find( '/' ) ) );
		std::string subtype( type.substr( media.size() + 1, std::string::npos ) );
		std::string icon_name( "gnome-mime-" );
		icon_name += media + "-" + subtype;
		if( gtk_icon_theme_has_icon( theme, icon_name.c_str() ) ) {
			//
			// We got one on the first try!
			//
			return adobe::value_t( icon_gtk_ptr_t( new icon_gtk( icon_name ) ) );
		}
		//
		// Let's try a shorter one.
		//
		icon_name = "gnome-mime-" + media;
		if( gtk_icon_theme_has_icon( theme, icon_name.c_str() ) ) {
			return adobe::value_t( icon_gtk_ptr_t( new icon_gtk( icon_name ) ) );
		}
		//
		// Perhaps this MIME type has a different name in GNOME.
		//
		mime_to_gnome_t::iterator i = gnome_icon_db_s.find( type );
		if( i != gnome_icon_db_s.end() ) {
			return adobe::value_t( icon_gtk_ptr_t( new icon_gtk( i->second ) ) );
		}
		//
		// Give the default.
		//
		return adobe::value_t( icon_gtk_ptr_t( new icon_gtk( fallback_s ) ) );
	}
	adobe::value_t checkPreview( const std::string& type, const boost::filesystem::path& path ) {
		//
		// Get the URI of the file we want the thumbnail of.
		// 
		gchar* uri( g_filename_to_uri( path.native_file_string().c_str(), 0, 0 ) );
		//
		// Does this URI have a failed thumbnail. If so then
		// we can just use that.
		//
		if( egg_pixbuf_has_failed_thumbnail( uri, bfs::last_write_time( path ), NULL ) ) {
			g_free( uri );
			return getIcon( type, path );
		}
		//
		// Attempt to load the thumbnail. If we get one then we can
		// return it.
		//
		GdkPixbuf* thumb = egg_pixbuf_load_thumbnail( uri, bfs::last_write_time( path ),
			EGG_PIXBUF_THUMBNAIL_NORMAL );
		g_free( uri );
		if( thumb ) return adobe::value_t( icon_gtk_ptr_t( new icon_gtk( "", thumb ) ) );
		return adobe::value_t();
	}
	adobe::value_t createPreview( const std::string& type, const boost::filesystem::path& path ) {
		//
		// I think this mutex eliminates the purpose of using threads.
		// We crash without it (in GLIB), though.
		//
		adobe::gtk_thread_lock_t	thread_lock;

		GdkPixbuf* thumb = egg_pixbuf_get_thumbnail_for_file( path.native_file_string().c_str(),
			EGG_PIXBUF_THUMBNAIL_NORMAL, NULL );
		if( thumb ) return adobe::value_t( icon_gtk_ptr_t( new icon_gtk( "", thumb ) ) );
		//
		// Fallback to regular icon.
		// 
		return getIcon( type, path );
	}
}

